/**
 * 文件名:  SSLHelper.java
 * 版权:    Copyright 2000-2011 Huawei Tech. Co. Ltd. All Rights Reserved.
 * 创建人:  tangzhixiong
 * 文件描述:  
 * 修改时间: 2011-6-23 上午11:07:37
 * 修改内容： 新增
 */
package com.huawei.virest.common.util;

import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.GZIPInputStream;

import javax.activation.MimetypesFileTypeMap;

import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
import org.apache.http.ParseException;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;

import com.huawei.virest.common.SystemInit;
import com.huawei.virest.common.constant.Constants;
import com.huawei.virest.common.domain.HttpResp;

/**
 * 
 * 类描述：HTTPS助手类
 * 
 * @author tangzhixiong
 * @version snsgw V100R001
 */
public final class HttpUtils
{
    private static HttpUtils instance = null;
    
    /* 日志实例 */
    private static Log logger = LogFactory.getLog(HttpUtils.class);
    
    /**
     * 构造方法
     */
    private HttpUtils()
    {
    }
    
    /**
     * 获得HttpUtils实例
     * @return HttpUtils
     */
    public static HttpUtils getInstance()
    {
        if (instance == null)
        {
            instance = new HttpUtils();
        }
        
        return instance;
    }
    
    private DefaultHttpClient createHttpclient()
    {
        HttpParams params = new BasicHttpParams();
        DefaultHttpClient httpclient = new DefaultHttpClient(params);
        httpclient.getParams()
                .setParameter(CoreProtocolPNames.PROTOCOL_VERSION,
                        HttpVersion.HTTP_1_1);
        return httpclient;
    }
    
    private HttpResp parseHttpResponse(HttpResponse response)
    {
        HttpResp resp = new HttpResp();
        HttpEntity entity = response.getEntity();
        
        logger.debug("Response statusLine: "
                + response.getStatusLine().toString());
        if (entity != null)
        {
            logger.debug("Response content length: "
                    + entity.getContentLength());
            
            resp.setHttpStatus(response.getStatusLine().getStatusCode());
            resp.setStatusText(response.getStatusLine().getReasonPhrase());
            try
            {
                //resp.setResponse(EntityUtils.toString(entity));
                //EntityUtils.consume(entity);
                logger.debug("StatusCode: " + resp.getHttpStatus());
                logger.debug("StatusText: " + resp.getStatusText());
                
                Header[] gzipHeader = response.getHeaders(Constants.CONTENT_ENCODING);
                for (int i = 0; i < gzipHeader.length; i++)
                {
                    if (gzipHeader[i] != null
                            && gzipHeader[i].getValue()
                                    .equalsIgnoreCase(Constants.GZIP))
                    {
                        byte[] b = new byte[1024];
                        StringBuffer buffer = new StringBuffer();
                        
                        GZIPInputStream gin = new GZIPInputStream(
                                entity.getContent());
                        
                        int len = 0;
                        while ((len = gin.read(b)) > 0)
                        {
                            buffer.append(new String(b, 0, len, Constants.DEFAULT_ENCODING));
                        }
                        
                        gin.close();
                        resp.setResponse(buffer.toString());
                        break;
                    }
                }
                
                if (Utils.isNull(resp.getResponse()))
                {
                    resp.setResponse(EntityUtils.toString(entity));
                }
                
                EntityUtils.consume(entity);
            }
            catch (ParseException e)
            {
                logger.error("parseHttpResponse ParseException! === "
                        + e.getMessage());
            }
            catch (IOException e)
            {
                logger.error("parseHttpResponse IOException! === "
                        + e.getMessage());
            }
        }
        else
        {
            logger.error("the response is null");
        }
        
        return resp;
    }
    
    /**
     * 请求方法
     * @param requestData 请求数据
     */
    @SuppressWarnings("unchecked")
    public HttpResp request(Map<String, Object> requestData)
    {
        String url = parseRequestParameter(requestData);
        String method = requestData.get(Constants.METHOD).toString();
        
        String body = null;
        if (null != requestData.get(Constants.BODY))
        {
            body = requestData.get(Constants.BODY).toString();
        }
        
        File attachment = null;
        if (null != requestData.get(Constants.ATTACHMENT))
        {
            attachment = new File(SystemInit.getConfig().getAbsolutePath()
                    + requestData.get(Constants.ATTACHMENT).toString());
        }
        
        Map<String, String> headerValues = (Map<String, String>)requestData.get(Constants.HEADERS);
        
        HttpResp resp = null;
        if (method.toLowerCase().equals(Constants.HTTPMethod.GET))
        {
            //Map<String, String> parameters = parseRequestURL(url);
            //url = cleanUrl(url);
            //resp = this.get(url, headerValues, parameters);
            resp = this.get(url, headerValues);
            
        }
        else if (method.toLowerCase().equals(Constants.HTTPMethod.DELETE))
        {
//            Map<String, String> parameters = parseRequestURL(url);
//            resp = this.delete(url, headerValues, parameters);
            resp = this.delete(url, headerValues);
        }
        else if (method.toLowerCase().equals(Constants.HTTPMethod.POST))
        {
            Map<String, Object> parameters = parseRequestBody(url,
                    body,
                    attachment);
            resp = this.post(url, headerValues, parameters);
        }
        else if (method.toLowerCase().equals(Constants.HTTPMethod.PUT))
        {
            Map<String, Object> parameters = parseRequestBody(url,
                    body,
                    attachment);
            resp = this.put(url, headerValues, parameters);
        }
        
        return resp;
    }
    
    /**
     * 重置请求Url
     * @param url URL
     * @return URI
     */
    @SuppressWarnings("unchecked")
    public static String parseRequestParameter(Map<String, Object> requestData)
    {
        String url = requestData.get(Constants.URL).toString();
        
        // 读取系统配置是否替换URL的IP地址端口及项目名称
        String baseUrl = SystemInit.getConfig().getBaseUrl();
        if (!Utils.isNull(baseUrl))
        {
            int idx = url.indexOf(Constants.Mark.DOUBLE_SLASH);
            idx = url.indexOf(Constants.Mark.SLASH, idx + 2);
            idx = url.indexOf(Constants.Mark.SLASH, idx + 1);
            String str = url.substring(0, idx);
            
            url = url.replace(str, baseUrl);//替换
        }
        
        // 读取系统配置是否替换Authorization
        String auth = SystemInit.getConfig().getAuthorization();
        if (!Utils.isNull(auth))
        {
            Map<String, String> headerValues = (Map<String, String>)requestData.get(Constants.HEADERS);
            if (auth != null
                    && headerValues.containsKey(Constants.AUTHORIZATION))
            {
                headerValues.put(Constants.AUTHORIZATION, auth);
            }
        }
        
        return url;
    }
    
    /**
     * 整理URL
     * @param url URL
     * @return URL
     *//*
    private String cleanUrl(String url)
    {
        int questionIdx = url.indexOf("?");
        
        if (questionIdx != -1)
        {
            url = url.substring(0, questionIdx);
        }
        
        return url;
    }
    
    *//**
     * 解析请求参数
     * @param url URL
     * @return 请求参数
     *//*
    private Map<String, String> parseRequestURL(String url)
    {
        Map<String, String> queryMap = new HashMap<String, String>();
        parseQueryString(url, queryMap);// 解析请求查询参数
        return queryMap;
    }
    
    *//**
     * 解析查询字符串
     * @param url 查询字符串
     * @param map 查询参数Map
     */
    @SuppressWarnings("unchecked")
    private void parseQueryString(String url, Map map)
    {
        int questionIdx = url.indexOf(Constants.Mark.QUESTION_MARK);
        if (questionIdx != -1)
        {
            String[] queries = url.substring(questionIdx + 1).split(Constants.Mark.ADDRESS_OF_OPERATOR);
            int len = queries.length;
            for (int i = 0; i < len; i++)
            {
                String[] attributes = queries[i].split(Constants.Mark.EQUAL_MARK);
                if (attributes.length > 0)
                {
                    String values = "";
                    if (attributes.length > 1)
                    {
                        values = attributes[1];
                    }
                    
                    map.put(attributes[0], values);
                }
            }
        }
    }
    
    /**
     * 解析请求消息体
     * @param body 消息体
     * @param attachment 文件Path
     * @return 消息体
     */
    private Map<String, Object> parseRequestBody(String url, String body,
            File attachment)
    {
        Map<String, Object> parameters = new HashMap<String, Object>();
        if (body != null)
        {
            parameters.put(Constants.BODY, body);
        }
        else
        {
            parseQueryString(url, parameters);
        }
        if (null != attachment)
        {
            parameters.put(Constants.FILE, attachment);
        }
        return parameters;
    }
    
    /**
     * get方法
     * @param url String
     * @param headerValues Map<String, String>
     * @param getParameters Map<String, String>
     * @return HttpResp
     * @throws IOException 
     * @throws ClientProtocolException 
     */
    public HttpResp get(String url, Map<String, String> headerValues,
            Map<String, String> getParameters)
    {
        //add get parameters to url
        StringBuffer urlWithQuery = new StringBuffer(url);
        if (getParameters != null)
        {
            boolean endsWithQuestion = false;
            if (url.indexOf(Constants.Mark.QUESTION_MARK) == -1)
            {
                urlWithQuery.append(Constants.Mark.QUESTION_MARK);
                endsWithQuestion = true;
            }
            for (String key : getParameters.keySet())
            {
                if (!endsWithQuestion)
                {
                    urlWithQuery.append(Constants.Mark.ADDRESS_OF_OPERATOR);
                }
                endsWithQuestion = false;
                
                urlWithQuery.append(Utils.urlencode(key));
                urlWithQuery.append(Constants.Mark.EQUAL_MARK);
                //urlWithQuery.append(Utils.urlencode(getParameters.get(key)));
                urlWithQuery.append(getParameters.get(key));
            }
        }
        HttpGet get = new HttpGet(urlWithQuery.toString());
        get.addHeader(Constants.ACCEPT_ENCODING, Constants.GZIP);
        
        //header
        if (headerValues != null)
        {
            for (String key : headerValues.keySet())
            {
                get.addHeader(key, headerValues.get(key));
            }
        }
        return executeMethod(get);
    }
    
    
    /**
     * get方法
     * @param url String
     * @param headerValues Map<String, String>
     * @return HttpResp
     * @throws IOException 
     * @throws ClientProtocolException 
     */
    public HttpResp get(String url, Map<String, String> headerValues)
    {
        HttpGet get = new HttpGet(url);
        get.addHeader(Constants.ACCEPT_ENCODING, Constants.GZIP);
        
        //header
        if (headerValues != null)
        {
            for (String key : headerValues.keySet())
            {
                get.addHeader(key, headerValues.get(key));
            }
        }
        return executeMethod(get);
    }
    
    /**
     * post 方法
     * @param url String
     * @param headerValues Map<String, String>
     * @param postParameters Map<String, Object>
     * @return HttpResp
     * @throws IOException 
     * @throws ClientProtocolException 
     */
    public HttpResp post(String url, Map<String, String> headerValues,
            Map<String, Object> postParameters)
    {
        HttpPost post = new HttpPost(url);
        post.addHeader(Constants.ACCEPT_ENCODING, Constants.GZIP);
        
        //header
        if (headerValues != null)
        {
            for (String key : headerValues.keySet())
            {
                post.addHeader(key, headerValues.get(key));
            }
        }
        
        //params
        if (postParameters != null)
        {
            //check file existence
            boolean fileExists = false;
            for (String key : postParameters.keySet())
            {
                if (postParameters.get(key).getClass() == File.class)
                {
                    fileExists = true;
                    break;
                }
            }
            
            //set post body
            if (postParameters != null)
            {
                if (fileExists)
                {
                    MultipartEntity multipartEntity = new MultipartEntity(
                            HttpMultipartMode.STRICT);
                    for (String key : postParameters.keySet())
                    {
                        Object value = postParameters.get(key);
                        if (value.getClass() == File.class)
                        {
                            File file = (File)value;
                            FileBody fileBody = new FileBody(
                                    file,
                                    file.getName(),
                                    new MimetypesFileTypeMap().getContentType(file),
                                    Constants.DEFAULT_ENCODING);
                            multipartEntity.addPart(key, fileBody);
                        }
                        else
                        {
                            try
                            {
                                multipartEntity.addPart(key, new StringBody(
                                        value.toString(),
                                        Charset.forName(Constants.DEFAULT_ENCODING)));
                            }
                            catch (UnsupportedEncodingException e)
                            {
                                logger.error(e.getMessage());
                            }
                        }
                    }
                    post.setEntity(multipartEntity);
                }
                else
                {
                    ArrayList<BasicNameValuePair> params = new ArrayList<BasicNameValuePair>();
                    for (String key : postParameters.keySet())
                    {
                        params.add(new BasicNameValuePair(key,
                                postParameters.get(key).toString()));
                    }
                    try
                    {
                        //                        UrlEncodedFormEntity urlEncodedFormEntity;
                        //                        urlEncodedFormEntity = new UrlEncodedFormEntity(params,
                        //                                "UTF-8");
                        //                        post.setEntity(urlEncodedFormEntity);
                        if (params.size() > 0)
                        {
                            BasicNameValuePair basicNameValuePair = params.get(0);
                            String json = basicNameValuePair.getValue();
                            StringEntity se = new StringEntity(json, Constants.DEFAULT_ENCODING);
                            se.setContentEncoding(new BasicHeader(
                                    HTTP.CONTENT_TYPE, Constants.CONTENT_TYPE));
                            post.setEntity(se);
                        }
                    }
                    catch (UnsupportedEncodingException e)
                    {
                        logger.error(e.toString());
                    }
                }
            }
        }
        
        return executeMethod(post);
    }
    
    /**
     * put方法
     * @param url String
     * @param headerValues Map<String, String>
     * @param putParameters Map<String, Object>
     * @return HttpResp HttpResp
     * @see [类、类#方法、类#成员]
     */
    public HttpResp put(String url, Map<String, String> headerValues,
            Map<String, Object> putParameters)
    {
        HttpPut put = new HttpPut(url);
        put.addHeader(Constants.ACCEPT_ENCODING, Constants.GZIP);
        
        //header
        if (headerValues != null)
        {
            for (String key : headerValues.keySet())
            {
                put.addHeader(key, headerValues.get(key));
            }
        }
        
        //params
        if (putParameters != null)
        {
            //check file existence
            boolean fileExists = false;
            for (String key : putParameters.keySet())
            {
                if (putParameters.get(key).getClass() == File.class)
                {
                    fileExists = true;
                    break;
                }
            }
            
            //set post body
            if (putParameters != null)
            {
                if (fileExists)
                {
                    MultipartEntity multipartEntity = new MultipartEntity(
                            HttpMultipartMode.STRICT);
                    for (String key : putParameters.keySet())
                    {
                        Object value = putParameters.get(key);
                        if (value.getClass() == File.class)
                        {
                            File file = (File)value;
                            FileBody fileBody = new FileBody(
                                    file,
                                    file.getName(),
                                    new MimetypesFileTypeMap().getContentType(file),
                                    Constants.DEFAULT_ENCODING);
                            multipartEntity.addPart(key, fileBody);
                        }
                        else
                        {
                            try
                            {
                                multipartEntity.addPart(key, new StringBody(
                                        value.toString()));
                            }
                            catch (UnsupportedEncodingException e)
                            {
                                logger.error(e.toString());
                            }
                        }
                    }
                    put.setEntity(multipartEntity);
                }
                else
                {
                    ArrayList<BasicNameValuePair> params = new ArrayList<BasicNameValuePair>();
                    for (String key : putParameters.keySet())
                    {
                        params.add(new BasicNameValuePair(key,
                                putParameters.get(key).toString()));
                    }
                    try
                    {
                        //                        UrlEncodedFormEntity urlEncodedFormEntity;
                        //                        urlEncodedFormEntity = new UrlEncodedFormEntity(params,
                        //                                "utf-8");
                        //                        put.setEntity(urlEncodedFormEntity);
                        if (params.size() > 0)
                        {
                            BasicNameValuePair basicNameValuePair = params.get(0);
                            String json = basicNameValuePair.getValue();
                            StringEntity se = new StringEntity(json, Constants.DEFAULT_ENCODING);
                            se.setContentEncoding(new BasicHeader(
                                    HTTP.CONTENT_TYPE, Constants.CONTENT_TYPE));
                            put.setEntity(se);
                        }
                    }
                    catch (UnsupportedEncodingException e)
                    {
                        logger.error(e.toString());
                    }
                }
            }
        }
        
        return executeMethod(put);
    }
    
    /**
     * delete方法
     * @param url URL
     * @param headerValues 消息头
     * @param deleteParameters 删除参数
     * @return HttpResp 响应
     */
    public HttpResp delete(String url, Map<String, String> headerValues,
            Map<String, String> deleteParameters)
    {
        //add get parameters to url
        StringBuffer urlWithQuery = new StringBuffer(url);
        if (deleteParameters != null)
        {
            boolean endsWithQuestion = false;
            if (url.indexOf(Constants.Mark.QUESTION_MARK) == -1)
            {
                urlWithQuery.append(Constants.Mark.QUESTION_MARK);
                endsWithQuestion = true;
            }
            for (String key : deleteParameters.keySet())
            {
                if (!endsWithQuestion)
                {
                    urlWithQuery.append(Constants.Mark.ADDRESS_OF_OPERATOR);
                }
                endsWithQuestion = false;
                
                urlWithQuery.append(Utils.urlencode(key));
                urlWithQuery.append(Constants.Mark.EQUAL_MARK);
                urlWithQuery.append(deleteParameters.get(key));
            }
        }
        HttpDelete delete = new HttpDelete(urlWithQuery.toString());
        delete.addHeader(Constants.ACCEPT_ENCODING, Constants.GZIP);
        
        //header
        if (headerValues != null)
        {
            for (String key : headerValues.keySet())
            {
                delete.addHeader(key, headerValues.get(key));
            }
        }
        
        return executeMethod(delete);
    }
    
    /**
     * delete方法
     * @param url URL
     * @param headerValues 消息头
     * @return HttpResp 响应
     */
    public HttpResp delete(String url, Map<String, String> headerValues)
    {
        HttpDelete delete = new HttpDelete(url);
        delete.addHeader(Constants.ACCEPT_ENCODING, Constants.GZIP);
        
        //header
        if (headerValues != null)
        {
            for (String key : headerValues.keySet())
            {
                delete.addHeader(key, headerValues.get(key));
            }
        }
        
        return executeMethod(delete);
    }
    
    /**
     * 执行方法
     * @param client 客户端
     * @param method Http方法
     * @return HttpResp 响应
     */
    private HttpResp executeMethod(HttpRequestBase method)
    {
        DefaultHttpClient client = createHttpclient();
        //response
        HttpResp resp = null;
        try
        {
            HttpResponse response = client.execute(method);
            resp = parseHttpResponse(response);
        }
        catch (IOException e)
        {
            logger.error(method.getMethod() + "() send " + method.getMethod()
                    + " request failed", e);
            resp = new HttpResp();
            resp.setHttpStatus(HttpStatus.SC_NOT_FOUND);
            resp.setStatusText(e.getMessage());
        }
        finally
        {
            client.getConnectionManager().shutdown();
        }
        
        return resp;
    }
}
